use std::error::Error;
use std::fs::{copy, create_dir, read_dir, read_to_string, remove_dir_all, remove_file, File};
use std::path::Path;
use std::process::Command;

use zip::ZipWriter;
use zip_extensions::write::ZipWriterExtensions;

use log::{error, info};

fn copy_directory(from_dir: &String, to_dir: &String) -> Result<String, Box<dyn Error>> {
    create_dir(to_dir)?;
    for entry in read_dir(from_dir)? {
        let e = entry?;
        if let Some(entry_name) = e.file_name().to_str() {
            if e.path().is_dir() {
                copy_directory(
                    &format!("{}/{}", from_dir, entry_name),
                    &format!("{}/{}", to_dir, entry_name),
                )?;
            } else {
                copy(
                    &format!("{}/{}", from_dir, entry_name),
                    &format!("{}/{}", to_dir, entry_name),
                )?;
            }
        }
    }
    return Ok("success".to_string());
}

use super::project_type::ProjectType;
pub struct VueToWarType {
    pub npm_command: String,
    pub vue_location: String,
    pub war_name: String,
}

impl ProjectType for VueToWarType {
    fn new() -> Self {
        let vue_location;
        match read_to_string("vue.location") {
            Ok(vls) => {
                vue_location = vls;
            }
            Err(error) => {
                error!("{:?}", error);
                panic!();
            }
        }

        let war_name;
        match read_to_string("war.name") {
            Ok(wn) => {
                war_name = wn;
            }
            Err(error) => {
                error!("{:?}", error);
                panic!()
            }
        }
        let npm_command;
        match read_to_string("npm.command") {
            Ok(nc) => {
                npm_command = nc;
            }
            Err(error) => {
                npm_command = String::from("npm");
                error!("{:?}", error);
            }
        }

        return Self {
            npm_command,
            war_name,
            vue_location,
        };
    }

    fn build(&self) -> Result<String, Box<dyn Error>> {
        info!("信息：开始”npm install“");
        let install_status = Command::new(self.npm_command.as_str())
            .current_dir(&self.vue_location)
            .arg("install")
            .spawn()
            .expect("信息：npm构建失败！")
            .wait()?;

        if install_status.success() {
            info!("信息：开始”npm run build");
            let build_status = Command::new(self.npm_command.as_str())
                .current_dir(&self.vue_location)
                .arg("run")
                .arg("build")
                .spawn()
                .expect("信息：npm构建失败！")
                .wait()?;

            if build_status.success() {
                let dist_path = Path::new("./dist");
                if dist_path.exists() {
                    if dist_path.is_file() {
                        remove_file(dist_path)?;
                    } else {
                        remove_dir_all(dist_path)?;
                    }
                }
                create_dir(dist_path)?;
                copy_directory(
                    &format!("./WebContent"),
                    &format!("./dist/{}", self.war_name),
                )?;
                for entry in read_dir(format!("{}/dist", self.vue_location))? {
                    let e = entry?;
                    if let Some(entry_name) = e.file_name().to_str() {
                        if e.path().is_dir() {
                            copy_directory(
                                &format!("{}/dist/{}", self.vue_location, entry_name),
                                &format!("./dist/{}/{}", self.war_name, entry_name),
                            )?;
                        } else {
                            copy(
                                &format!("{}/dist/{}", self.vue_location, entry_name),
                                &format!("./dist/{}/{}", self.war_name, entry_name),
                            )?;
                        }
                    }
                }

                let file = File::create(&format!("./dist/{}.war", self.war_name)).unwrap();
                let mut zip = ZipWriter::new(file);
                zip.create_from_directory(
                    &Path::new(&format!("./dist/{}", self.war_name)).to_path_buf(),
                )
                .unwrap()
            }
        }
        return Ok(String::from("success"));
    }
}
